package com.super8.super8newtv.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.super8.super8newtv.bean.TokenBean;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import androidx.annotation.NonNull;
import androidx.collection.SimpleArrayMap;

/**
 * 业务描述:缓存工具
 *
 * @创 建 人: zhangl
 * @创建时间: 2020-07-07 18:08:02
 */
public class CacheUtil {

    public static ACache getCache(Context montext) {
        return ACache.get(montext,"networkCache", 1024 * 1024 * 50, Integer.MAX_VALUE);
    }

    public static ACache getCache(Context montext,String cacheName) {
        return ACache.get(montext,cacheName, 1024 * 1024 * 50, Integer.MAX_VALUE);
    }

    /**
    * 业务描述:获取 Token信息
    * @param :
    * @创 建 人: zhangl
    * @创建时间:2020-07-07 18:45:31
    * @return
    */
    public static TokenBean getTokenCache(Context montext) {
        //当前选中的
        String strTokenBean= getCache(montext).getString("TokenBean");
        if(TextUtils.isEmpty(strTokenBean)){
            String refreshToken=getCache(montext).getString("RefreshToken");
            if(TextUtils.isEmpty(refreshToken)){
                return null;
            }else{
                TokenBean bean=new TokenBean();
                bean.setRefresh_token(refreshToken);
                return bean;
            }
        }else{
            return new Gson().fromJson(strTokenBean,TokenBean.class);
        }
    }
    /**
     * 业务描述:保存 Token信息
     *
     * @创 建 人: zhangl
     * @创建时间: 2020-05-18 14:50:33
     */
    public static void setTokenCache(Context montext, TokenBean bean) {
        int expiresTime=bean.getExpires_in()-30*ACache.SEC ;// 提前30秒
        if(expiresTime<=0){
            expiresTime=bean.getExpires_in();
        }
        getCache(montext).put("TokenBean",new Gson().toJson(bean),expiresTime);//刷新token
        getCache(montext).put("RefreshToken",bean.getRefresh_token());//刷新token
    }
    /**
     * 业务描述:清除 Token信息
     *
     * @创 建 人: zhangl
     * @创建时间: 2020-05-18 14:52:40
     */
    public static void clearTokenCache(Context montext) {
        getCache(montext).remove("TokenBean");
        getCache(montext).remove("RefreshToken");
    }

    /**
     * 业务描述:缓存相关工具类【键值对存储】
     *
     * @创 建 人: zhangl
     * @创建时间: 2020-05-11 11:36:58
     */
    public static final class ACache {

        private static final long DEFAULT_MAX_SIZE = Long.MAX_VALUE;
        private static final int DEFAULT_MAX_COUNT = Integer.MAX_VALUE;

        public static final int SEC = 1;
        public static final int MIN = 60;
        public static final int HOUR = 3600;
        public static final int DAY = 86400;

        private static final SimpleArrayMap<String, ACache> CACHE_MAP = new SimpleArrayMap<>();
        private CacheManager mCacheManager;

        /**
         * 获取缓存实例
         * <p>在 /data/data/com.xxx.xxx/cache/ACache 目录</p>
         * <p>缓存尺寸不限</p>
         * <p>缓存个数不限</p>
         *
         * @return {@link ACache}
         */
        public static ACache get(Context context) {
            return get(context,"", DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);
        }

        /**
         * 获取缓存实例
         * <p>在 /data/data/com.xxx.xxx/cache/cacheName 目录</p>
         * <p>缓存尺寸不限</p>
         * <p>缓存个数不限</p>
         *
         * @param cacheName 缓存目录名
         * @return {@link ACache}
         */
        public static ACache get(Context context,final String cacheName) {
            return get(context,cacheName, DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);
        }

        /**
         * 获取缓存实例
         * <p>在 /data/data/com.xxx.xxx/cache/ACache </p>
         *
         * @param maxSize  最大缓存尺寸，单位字节
         * @param maxCount 最大缓存个数
         * @return {@link ACache}
         */
        public static ACache get(Context context,final long maxSize, final int maxCount) {
            return get(context,"", maxSize, maxCount);
        }

        /**
         * 获取缓存实例
         * <p>在 /data/data/com.xxx.xxx/cache/cacheName 目录</p>
         *
         * @param cacheName 缓存目录名
         * @param maxSize   最大缓存尺寸，单位字节
         * @param maxCount  最大缓存个数
         * @return {@link ACache}
         */
        public static ACache get(Context context,String cacheName, final long maxSize, final int maxCount) {
            if (isSpace(cacheName)) cacheName = "ACache";
            File file = new File(context.getCacheDir(), cacheName);
            return get(file, maxSize, maxCount);
        }

        /**
         * 获取缓存实例
         * <p>在 cacheDir 目录</p>
         * <p>缓存尺寸不限</p>
         * <p>缓存个数不限</p>
         *
         * @param cacheDir 缓存目录
         * @return {@link ACache}
         */
        public static ACache get(@NonNull final File cacheDir) {
            return get(cacheDir, DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);
        }

        /**
         * 获取缓存实例
         * <p>在 cacheDir 目录</p>
         *
         * @param cacheDir 缓存目录
         * @param maxSize  最大缓存尺寸，单位字节
         * @param maxCount 最大缓存个数
         * @return {@link ACache}
         */
        public static ACache get(@NonNull final File cacheDir,
                                 final long maxSize,
                                 final int maxCount) {
            final String cacheKey = cacheDir.getAbsoluteFile() + "_" + Process.myPid();
            ACache cache = CACHE_MAP.get(cacheKey);
            if (cache == null) {
                cache = new ACache(cacheDir, maxSize, maxCount);
                CACHE_MAP.put(cacheKey, cache);
            }
            return cache;
        }

        private ACache(@NonNull final File cacheDir, final long maxSize, final int maxCount) {
            if (!cacheDir.exists() && !cacheDir.mkdirs()) {
                throw new RuntimeException("can't make dirs in " + cacheDir.getAbsolutePath());
            }
            mCacheManager = new CacheManager(cacheDir, maxSize, maxCount);
        }

        ///////////////////////////////////////////////////////////////////////////
        // bytes 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入字节数组
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final byte[] value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入字节数组
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key, @NonNull byte[] value, final int saveTime) {
            if (value.length <= 0) return;
            if (saveTime >= 0) value = CacheHelper.newByteArrayWithTime(saveTime, value);
            File file = mCacheManager.getFileBeforePut(key);
            CacheHelper.writeFileFromBytes(file, value);
            mCacheManager.updateModify(file);
            mCacheManager.put(file);
        }

        /**
         * 缓存中读取字节数组
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public byte[] getBytes(@NonNull final String key) {
            return getBytes(key, null);
        }

        /**
         * 缓存中读取字节数组
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {
            final File file = mCacheManager.getFileIfExists(key);
            if (file == null) return defaultValue;
            byte[] data = CacheHelper.readFile2Bytes(file);
            if (CacheHelper.isDue(data)) {
                mCacheManager.removeByKey(key);
                return defaultValue;
            }
            mCacheManager.updateModify(file);
            return CacheHelper.getDataWithoutDueTime(data);
        }

        ///////////////////////////////////////////////////////////////////////////
        // String 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 String
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final String value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 String
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key, @NonNull final String value, final int saveTime) {
            put(key, CacheHelper.string2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 String
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public String getString(@NonNull final String key) {
            return getString(key, null);
        }

        /**
         * 缓存中读取 String
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public String getString(@NonNull final String key, final String defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2String(bytes);
        }

        ///////////////////////////////////////////////////////////////////////////
        // JSONObject 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 JSONObject
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final JSONObject value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 JSONObject
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key,
                        @NonNull final JSONObject value,
                        final int saveTime) {
            put(key, CacheHelper.jsonObject2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 JSONObject
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public JSONObject getJSONObject(@NonNull final String key) {
            return getJSONObject(key, null);
        }

        /**
         * 缓存中读取 JSONObject
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2JSONObject(bytes);
        }


        ///////////////////////////////////////////////////////////////////////////
        // JSONArray 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 JSONArray
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final JSONArray value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 JSONArray
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key, @NonNull final JSONArray value, final int saveTime) {
            put(key, CacheHelper.jsonArray2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 JSONArray
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public JSONArray getJSONArray(@NonNull final String key) {
            return getJSONArray(key, null);
        }

        /**
         * 缓存中读取 JSONArray
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2JSONArray(bytes);
        }


        ///////////////////////////////////////////////////////////////////////////
        // Bitmap 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 Bitmap
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final Bitmap value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 Bitmap
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key, @NonNull final Bitmap value, final int saveTime) {
            put(key, CacheHelper.bitmap2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 Bitmap
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public Bitmap getBitmap(@NonNull final String key) {
            return getBitmap(key, null);
        }

        /**
         * 缓存中读取 Bitmap
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2Bitmap(bytes);
        }

        ///////////////////////////////////////////////////////////////////////////
        // Drawable 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 Drawable
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final Drawable value) {
            put(key, CacheHelper.drawable2Bytes(value));
        }

        /**
         * 缓存中写入 Drawable
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key, @NonNull final Drawable value, final int saveTime) {
            put(key, CacheHelper.drawable2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 Drawable
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public Drawable getDrawable(@NonNull final String key) {
            return getDrawable(key, null);
        }

        /**
         * 缓存中读取 Drawable(注意：须初始context对象，否则会报错)
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2Drawable(bytes);
        }

        ///////////////////////////////////////////////////////////////////////////
        // Parcelable 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 Parcelable
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final Parcelable value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 Parcelable
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key,
                        @NonNull final Parcelable value,
                        final int saveTime) {
            put(key, CacheHelper.parcelable2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 Parcelable
         *
         * @param key     键
         * @param creator 建造器
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public <T> T getParcelable(@NonNull final String key,
                                   @NonNull final Parcelable.Creator<T> creator) {
            return getParcelable(key, creator, null);
        }

        /**
         * 缓存中读取 Parcelable
         *
         * @param key          键
         * @param creator      建造器
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public <T> T getParcelable(@NonNull final String key,
                                   @NonNull final Parcelable.Creator<T> creator,
                                   final T defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2Parcelable(bytes, creator);
        }

        ///////////////////////////////////////////////////////////////////////////
        // Serializable 读写
        ///////////////////////////////////////////////////////////////////////////

        /**
         * 缓存中写入 Serializable
         *
         * @param key   键
         * @param value 值
         */
        public void put(@NonNull final String key, @NonNull final Serializable value) {
            put(key, value, -1);
        }

        /**
         * 缓存中写入 Serializable
         *
         * @param key      键
         * @param value    值
         * @param saveTime 保存时长，单位：秒
         */
        public void put(@NonNull final String key,
                        @NonNull final Serializable value,
                        final int saveTime) {
            put(key, CacheHelper.serializable2Bytes(value), saveTime);
        }

        /**
         * 缓存中读取 Serializable
         *
         * @param key 键
         * @return 存在且没过期返回对应值，否则返回{@code null}
         */
        public Object getSerializable(@NonNull final String key) {
            return getSerializable(key, null);
        }

        /**
         * 缓存中读取 Serializable
         *
         * @param key          键
         * @param defaultValue 默认值
         * @return 存在且没过期返回对应值，否则返回默认值{@code defaultValue}
         */
        public Object getSerializable(@NonNull final String key, final Object defaultValue) {
            byte[] bytes = getBytes(key);
            if (bytes == null) return defaultValue;
            return CacheHelper.bytes2Object(getBytes(key));
        }

        /**
         * 获取缓存大小
         * <p>单位：字节</p>
         * <p>调用了 Thread.join()，需异步调用，否则可能主线程会卡顿</p>
         *
         * @return 缓存大小
         */
        public long getCacheSize() {
            return mCacheManager.getCacheSize();
        }

        /**
         * 获取缓存个数
         * <p>调用了 Thread.join()，需异步调用，否则可能主线程会卡顿</p>
         *
         * @return 缓存个数
         */
        public int getCacheCount() {
            return mCacheManager.getCacheCount();
        }

        /**
         * 根据键值移除缓存
         *
         * @param key 键
         * @return {@code true}: 移除成功<br>{@code false}: 移除失败
         */
        public boolean remove(@NonNull final String key) {
            return mCacheManager.removeByKey(key);
        }

        /**
         * 清除所有缓存
         *
         * @return {@code true}: 清除成功<br>{@code false}: 清除失败
         */
        public boolean clear() {
            return mCacheManager.clear();
        }

        private class CacheManager {
            private final AtomicLong cacheSize;
            private final AtomicInteger cacheCount;
            private final long sizeLimit;
            private final int countLimit;
            private final Map<File, Long> lastUsageDates
                    = Collections.synchronizedMap(new HashMap<File, Long>());
            private final File cacheDir;
            private final Thread mThread;

            private CacheManager(final File cacheDir, final long sizeLimit, final int countLimit) {
                this.cacheDir = cacheDir;
                this.sizeLimit = sizeLimit;
                this.countLimit = countLimit;
                cacheSize = new AtomicLong();
                cacheCount = new AtomicInteger();
                mThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int size = 0;
                        int count = 0;
                        final File[] cachedFiles = cacheDir.listFiles();
                        if (cachedFiles != null) {
                            for (File cachedFile : cachedFiles) {
                                size += cachedFile.length();
                                count += 1;
                                lastUsageDates.put(cachedFile, cachedFile.lastModified());
                            }
                            cacheSize.getAndAdd(size);
                            cacheCount.getAndAdd(count);
                        }
                    }
                });
                mThread.start();
            }

            private long getCacheSize() {
                try {
                    mThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return cacheSize.get();
            }

            private int getCacheCount() {
                try {
                    mThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return cacheCount.get();
            }

            private File getFileBeforePut(final String key) {
                File file = new File(cacheDir, String.valueOf(key.hashCode()));
                if (file.exists()) {
                    cacheCount.addAndGet(-1);
                    cacheSize.addAndGet(-file.length());
                }
                return file;
            }

            private File getFileIfExists(final String key) {
                File file = new File(cacheDir, String.valueOf(key.hashCode()));
                if (!file.exists()) return null;
                return file;
            }

            private void put(final File file) {
                cacheCount.addAndGet(1);
                cacheSize.addAndGet(file.length());
                while (cacheCount.get() > countLimit || cacheSize.get() > sizeLimit) {
                    cacheSize.addAndGet(-removeOldest());
                    cacheCount.addAndGet(-1);
                }
            }

            private void updateModify(final File file) {
                Long millis = System.currentTimeMillis();
                file.setLastModified(millis);
                lastUsageDates.put(file, millis);
            }

            private boolean removeByKey(final String key) {
                File file = getFileIfExists(key);
                if (file == null) return true;
                if (!file.delete()) return false;
                cacheSize.addAndGet(-file.length());
                cacheCount.addAndGet(-1);
                lastUsageDates.remove(file);
                return true;
            }

            private boolean clear() {
                File[] files = cacheDir.listFiles();
                if (files == null || files.length <= 0) return true;
                boolean flag = true;
                for (File file : files) {
                    if (!file.delete()) {
                        flag = false;
                        continue;
                    }
                    cacheSize.addAndGet(-file.length());
                    cacheCount.addAndGet(-1);
                    lastUsageDates.remove(file);
                }
                if (flag) {
                    lastUsageDates.clear();
                    cacheSize.set(0);
                    cacheCount.set(0);
                }
                return flag;
            }

            /**
             * 移除旧的文件
             *
             * @return 移除的字节数
             */
            private long removeOldest() {
                if (lastUsageDates.isEmpty()) return 0;
                Long oldestUsage = Long.MAX_VALUE;
                File oldestFile = null;
                Set<Map.Entry<File, Long>> entries = lastUsageDates.entrySet();
                synchronized (lastUsageDates) {
                    for (Map.Entry<File, Long> entry : entries) {
                        Long lastValueUsage = entry.getValue();
                        if (lastValueUsage < oldestUsage) {
                            oldestUsage = lastValueUsage;
                            oldestFile = entry.getKey();
                        }
                    }
                }
                if (oldestFile == null) return 0;
                long fileSize = oldestFile.length();
                if (oldestFile.delete()) {
                    lastUsageDates.remove(oldestFile);
                    return fileSize;
                }
                return 0;
            }
        }

        private static class CacheHelper {

            static final int timeInfoLen = 14;
            static Context cxt;

            public CacheHelper(Context context) {
                cxt = context;
            }

            private static byte[] newByteArrayWithTime(final int second, final byte[] data) {
                byte[] time = createDueTime(second).getBytes();
                byte[] content = new byte[time.length + data.length];
                System.arraycopy(time, 0, content, 0, time.length);
                System.arraycopy(data, 0, content, time.length, data.length);
                return content;
            }

            /**
             * 创建过期时间
             *
             * @param second 秒
             * @return _$millis$_
             */
            private static String createDueTime(final int second) {
                return String.format(
                        Locale.getDefault(), "_$%010d$_",
                        System.currentTimeMillis() / 1000 + second
                );
            }

            /**
             * 是否过期
             *
             * @param data
             * @return
             */
            private static boolean isDue(final byte[] data) {
                long millis = getDueTime(data);
                return millis != -1 && System.currentTimeMillis() > millis;
            }

            /**
             * 获取过期时间
             *
             * @param data
             * @return
             */
            private static long getDueTime(final byte[] data) {
                if (hasTimeInfo(data)) {
                    String millis = new String(copyOfRange(data, 2, 12));
                    try {
                        return Long.parseLong(millis) * 1000;
                    } catch (NumberFormatException e) {
                        return -1;
                    }
                }
                return -1;
            }

            /**
             * 获取没有过期时间的数据
             *
             * @param data
             * @return
             */
            private static byte[] getDataWithoutDueTime(final byte[] data) {
                if (hasTimeInfo(data)) {
                    return copyOfRange(data, timeInfoLen, data.length);
                }
                return data;
            }

            private static byte[] copyOfRange(final byte[] original, final int from, final int to) {
                int newLength = to - from;
                if (newLength < 0) throw new IllegalArgumentException(from + " > " + to);
                byte[] copy = new byte[newLength];
                System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
                return copy;
            }

            /**
             * 是否含有时间信息
             *
             * @param data
             * @return
             */
            private static boolean hasTimeInfo(final byte[] data) {
                return data != null
                        && data.length >= timeInfoLen
                        && data[0] == '_'
                        && data[1] == '$'
                        && data[12] == '$'
                        && data[13] == '_';
            }

            /**
             * 写入数据到文件
             *
             * @param file
             * @param bytes
             */
            private static void writeFileFromBytes(final File file, final byte[] bytes) {
                FileChannel fc = null;
                try {
                    fc = new FileOutputStream(file, false).getChannel();
                    fc.write(ByteBuffer.wrap(bytes));
                    fc.force(true);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    CloseUtils.closeIO(fc);
                }
            }

            private static byte[] readFile2Bytes(final File file) {
                FileChannel fc = null;
                try {
                    fc = new RandomAccessFile(file, "r").getChannel();
                    int size = (int) fc.size();
                    MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
                    byte[] data = new byte[size];
                    mbb.get(data, 0, size);
                    return data;
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    CloseUtils.closeIO(fc);
                }
            }

            private static byte[] string2Bytes(final String string) {
                if (string == null) return null;
                return string.getBytes();
            }

            private static String bytes2String(final byte[] bytes) {
                if (bytes == null) return null;
                return new String(bytes);
            }

            private static byte[] jsonObject2Bytes(final JSONObject jsonObject) {
                if (jsonObject == null) return null;
                return jsonObject.toString().getBytes();
            }

            private static JSONObject bytes2JSONObject(final byte[] bytes) {
                if (bytes == null) return null;
                try {
                    return new JSONObject(new String(bytes));
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

            private static byte[] jsonArray2Bytes(final JSONArray jsonArray) {
                if (jsonArray == null) return null;
                return jsonArray.toString().getBytes();
            }

            private static JSONArray bytes2JSONArray(final byte[] bytes) {
                if (bytes == null) return null;
                try {
                    return new JSONArray(new String(bytes));
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

            private static byte[] parcelable2Bytes(final Parcelable parcelable) {
                if (parcelable == null) return null;
                Parcel parcel = Parcel.obtain();
                parcelable.writeToParcel(parcel, 0);
                byte[] bytes = parcel.marshall();
                parcel.recycle();
                return bytes;
            }

            private static <T> T bytes2Parcelable(final byte[] bytes,
                                                  final Parcelable.Creator<T> creator) {
                if (bytes == null) return null;
                Parcel parcel = Parcel.obtain();
                parcel.unmarshall(bytes, 0, bytes.length);
                parcel.setDataPosition(0);
                T result = creator.createFromParcel(parcel);
                parcel.recycle();
                return result;
            }

            private static byte[] serializable2Bytes(final Serializable serializable) {
                if (serializable == null) return null;
                ByteArrayOutputStream baos;
                ObjectOutputStream oos = null;
                try {
                    oos = new ObjectOutputStream(baos = new ByteArrayOutputStream());
                    oos.writeObject(serializable);
                    return baos.toByteArray();
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    CloseUtils.closeIO(oos);
                }
            }

            private static Object bytes2Object(final byte[] bytes) {
                if (bytes == null) return null;
                ObjectInputStream ois = null;
                try {
                    ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                    return ois.readObject();
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    CloseUtils.closeIO(ois);
                }
            }

            private static byte[] bitmap2Bytes(final Bitmap bitmap) {
                if (bitmap == null) return null;
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
                return baos.toByteArray();
            }

            private static Bitmap bytes2Bitmap(final byte[] bytes) {
                return (bytes == null || bytes.length == 0)
                        ? null
                        : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            }

            private static byte[] drawable2Bytes(final Drawable drawable) {
                return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable));
            }

            private static Drawable bytes2Drawable(final byte[] bytes) {
                return bytes == null ? null : bitmap2Drawable(bytes2Bitmap(bytes));
            }

            private static Bitmap drawable2Bitmap(final Drawable drawable) {
                if (drawable instanceof BitmapDrawable) {
                    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
                    if (bitmapDrawable.getBitmap() != null) {
                        return bitmapDrawable.getBitmap();
                    }
                }
                Bitmap bitmap;
                if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
                    bitmap = Bitmap.createBitmap(
                            1,
                            1,
                            drawable.getOpacity() != PixelFormat.OPAQUE
                                    ? Bitmap.Config.ARGB_8888
                                    : Bitmap.Config.RGB_565
                    );
                } else {
                    bitmap = Bitmap.createBitmap(
                            drawable.getIntrinsicWidth(),
                            drawable.getIntrinsicHeight(),
                            drawable.getOpacity() != PixelFormat.OPAQUE
                                    ? Bitmap.Config.ARGB_8888
                                    : Bitmap.Config.RGB_565
                    );
                }
                Canvas canvas = new Canvas(bitmap);
                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                drawable.draw(canvas);
                return bitmap;
            }

            private static Drawable bitmap2Drawable(final Bitmap bitmap) {
                return bitmap == null
                        ? null
                        : new BitmapDrawable(cxt.getResources(), bitmap);
            }
        }

        private static boolean isSpace(final String s) {
            if (s == null) return true;
            for (int i = 0, len = s.length(); i < len; ++i) {
                if (!Character.isWhitespace(s.charAt(i))) {
                    return false;
                }
            }
            return true;
        }
    }

}
